use std::fs::File;
use std::io::Read;
use std::io::Write;
use std::io::Seek;
use std::io::BufReader;
use std::io::BufWriter;
use std::io::ErrorKind;

use crate::constants::BACKUP_CONTAINER_ID_RECIPE_LOG;
use crate::constants::RESTORE_CONTAINER_ID_RECIPE_LOG;

use super::RecipeError;
use super::RecipeResult;
use super::container::ContainerId;

use std::path::Path;

use util::RawData;

use super::constants::CON_RECORD_BUF_LEN;

pub struct ConRecipeWriter {
	output:BufWriter<File>,
}

impl ConRecipeWriter {
	pub fn from_path<P:AsRef<Path>+?Sized>(path:&P) -> RecipeResult<Self> {
		Ok(Self {
			output:BufWriter::with_capacity(
				CON_RECORD_BUF_LEN,
				File::create(path).map_err(|e|RecipeError::ConWriterOpen(e))?,
			),
		})
	}
	
	pub fn from_file(f:File) -> Self {
		Self {
			output:BufWriter::with_capacity(CON_RECORD_BUF_LEN,f),
		}
	}

	pub fn cursor(&mut self) -> std::io::Result<u64> {
		self.output.stream_position()
	}
	
	pub fn append(&mut self,id:ContainerId) -> RecipeResult<()> {
		log::info!(target:BACKUP_CONTAINER_ID_RECIPE_LOG,"container id recipe is written with {} at {}\n",id,self.cursor().unwrap());
		self.output.write(id.as_raw_data()).map_err(|e|RecipeError::AppendCon(id, e))?;
		Ok(())
	}
}


pub struct ConRecipeReader {
	input:BufReader<File>,
}


impl ConRecipeReader {
	pub fn from_path<P:AsRef<Path>+?Sized>(path:&P) -> RecipeResult<Self> {
		Ok(Self {
			input:BufReader::with_capacity(
				CON_RECORD_BUF_LEN,
				File::open(path).map_err(|e|RecipeError::ConReaderOpen(e))?,
			),
		})
	}
	
	pub fn from_file(f:File) -> Self {
		Self {
			input:BufReader::with_capacity(CON_RECORD_BUF_LEN,f),
		}
	}
	
	pub fn cursor(&mut self) -> std::io::Result<u64> {
		self.input.stream_position()
	}

	pub fn get(&mut self) -> RecipeResult<Option<ContainerId>> {
		let mut id:ContainerId = 0;
		match self.input.read_exact(id.as_raw_mut())  {
			Ok(()) => {	
				log::info!(target:RESTORE_CONTAINER_ID_RECIPE_LOG,"container id recipe is written with {} at {}\n",id,self.cursor().unwrap());
				Ok(Some(id))
			}
			Err(e) => {
				match e.kind() {
					ErrorKind::UnexpectedEof => { Ok(None) }
					_ => { Err(RecipeError::GetCon(e)) } 
				}
			}
		}
	}
}
